package controller;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

import model.Client;
import model.Email;
import model.Server;

public class ClientListener implements Runnable {
	
	
	private Client client;
	private Server server;
	private ObjectInputStream clientReader;
	
	
	/**
	 * Constructor.
	 * 
	 * @param client
	 * @param server
	 */
	public ClientListener(Client client, Server server){
		this.client = client;
		this.server = server;
		clientReader = null;
	}

	@Override
	public void run() {

			try {
				startListening();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				server.destroyConnectedClient(client);
			}

	}
	
	/**
	 * Reads from the input buffer and takes appropriate actions
	 * @throws IOException 
	 * @throws ClassNotFoundException 
	 */
	private void startListening() throws IOException, ClassNotFoundException{
		
		System.out.println("Server: Listening from " + client.getName());
		clientReader = new ObjectInputStream(client.getSocket().getInputStream());

		while (true) {
			String message = (String) clientReader.readObject();
			handleMessage(message);
		}
	}

	/**
	 * Handles the message that came in from the client
	 * 
	 * @param message
	 * @throws IOException 
	 * @throws ClassNotFoundException 
	 */
	private void handleMessage(String message) throws ClassNotFoundException, IOException {
		
		switch (message) {

			case "emails": {
				
				System.out.println("Message type was EMAILS!! and the emails were...");
				ArrayList<Email> emailsReceived = (ArrayList<Email>) clientReader.readObject();
				
				for(Email em : emailsReceived){
					System.out.println(em.toString());
				}
				
				
				System.out.println("Now dispatching these emails... ");
				dispatchEmails(emailsReceived);
			}
	
			default: {
				System.out.println("Unknown message from client");
			}
	}
		
	}
	
	
	
	

	/**
	 * Creates a new email dispatcher and passes on the emails to it
	 * @param readObject
	 */
	private void dispatchEmails(ArrayList<Email> emailsReceived) {
		
		System.out.println("Connected clients number is: " + server.getConnectedClients().size());
		
		// Loop through all connected clients
		for (Client connectedClient : server.getConnectedClients()){
			
			System.out.println("-- Found client: " + connectedClient.getName());
			
			
			// Get emails that belong to this client
			ArrayList<Email> emailsForThisClient = new ArrayList<Email>();
			emailsForThisClient = filterEmailsForClient(connectedClient.getName(), emailsReceived);
			
			if (emailsForThisClient.size() > 0){
				try {
					
					client.getOutStream().writeObject(new String("emails"));
					client.getOutStream().writeObject(emailsForThisClient);

					for(Email em : emailsForThisClient){
						em.removePendingRecepient(connectedClient.getName());
					}
				} catch (IOException e) {
					System.out.println("--- ???????? -------");
				}
				
			}

		}
		
		// Once all connected clients have been sent their emails,
		// we would be left with emails with pending members. All
		// These emails need to be saved to the database and also
		// kept in the emailsToSend array in the server. The emails
		// which have no pending members should be deleted since 
		// they are gone.
		
		List<Email> emailsToPersist = new ArrayList<Email>();
		for (Email em : emailsReceived){
			if (!em.hasNoMorePendingRecepients()){
				emailsToPersist.add(em);
			}
		}
		
		server.persistEmailsToBeSent(emailsToPersist);
		
		
		// Log stats
		int totalEmails = emailsReceived.size(); 
		int countUndispatchedEmails = emailsToPersist.size();
		int countDispatchedEmails = totalEmails - countUndispatchedEmails; 

		System.out.println("Server: Email dispatcher stats:\n"+
					"---- Total emails " + totalEmails + "\n"+
					"---- Dispatched emails " + countDispatchedEmails + "\n"+
					"---- Persisted emails " + countUndispatchedEmails + "\n\n");
	}

	
	

	/**
	 * Returns a subset of emails in which this client is in pending list
	 * 
	 * @param username
	 * @return
	 */
	private ArrayList<Email> filterEmailsForClient(String username, ArrayList<Email> emailsList){
		
		ArrayList<Email> toReturn = new ArrayList<Email>();
		
		for( Email email : emailsList){
			if (email.hasPendingRecepient( username )){
				toReturn.add(email);
			}
		}
		return toReturn;
	}
}
